package jxtend.eclipse.jdt.hook;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import jxtend.processor.eclipse.jdt.dom.ASTNodeTypeAndFlags;
import jxtend.processor.eclipse.jdt.dom.MarkJxtendGeneratedASTVisitorMethodInterceptor;
import jxtend.processor.eclipse.utils.ASTNodeUtils;
import jxtend.processor.eclipse.utils.EclipseProcessorUtils;
import jxtend.processor.eclipse.visitor.handler.impl.ASTNodeBits;
import jxtend.processor.eclipse.visitor.handler.impl.JxtendTypeConstants;
import jxtend.processor.reflect.ReflectField;
import jxtend.processor.reflect.ReflectMethod;
import jxtend.processor.reflect.ReflectMethod.ResultMethod1Args;
import jxtend.processor.reflect.ReflectMethod.VoidMethod0Args;
import jxtend.processor.utils.ReflectUtils;

import org.aspectj.lang.ProceedingJoinPoint;
import org.eclipse.core.internal.resources.Workspace;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IRegion;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeRoot;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.ImportDeclaration;
import org.eclipse.jdt.internal.codeassist.CompletionEngine;
import org.eclipse.jdt.internal.codeassist.ISelectionRequestor;
import org.eclipse.jdt.internal.codeassist.SelectionEngine;
import org.eclipse.jdt.internal.codeassist.complete.CompletionNodeFound;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnQualifiedNameReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionOnSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.complete.CompletionParser;
import org.eclipse.jdt.internal.codeassist.impl.AssistParser;
import org.eclipse.jdt.internal.codeassist.impl.Engine;
import org.eclipse.jdt.internal.codeassist.select.SelectionNodeFound;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnNameOfMemberValuePair;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnParameterizedQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnParameterizedSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnQualifiedTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnQualifiedAllocationExpression;
import org.eclipse.jdt.internal.codeassist.select.SelectionOnSingleTypeReference;
import org.eclipse.jdt.internal.codeassist.select.SelectionParser;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.Compiler;
import org.eclipse.jdt.internal.compiler.SourceElementParser;
import org.eclipse.jdt.internal.compiler.ast.ASTNode;
import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.AbstractVariableDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Annotation;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.FieldDeclaration;
import org.eclipse.jdt.internal.compiler.ast.ForeachStatement;
import org.eclipse.jdt.internal.compiler.ast.ImportReference;
import org.eclipse.jdt.internal.compiler.ast.LocalDeclaration;
import org.eclipse.jdt.internal.compiler.ast.MessageSend;
import org.eclipse.jdt.internal.compiler.ast.QualifiedTypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.env.ISourceType;
import org.eclipse.jdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.jdt.internal.compiler.lookup.BinaryTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.BlockScope;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.InvocationSite;
import org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment;
import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.PackageBinding;
import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
import org.eclipse.jdt.internal.compiler.parser.Parser;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.core.CompilationUnit;
import org.eclipse.jdt.internal.core.CompilationUnitProblemFinder;
import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner;
import org.eclipse.jdt.internal.core.JavaModelManager;
import org.eclipse.jdt.internal.core.JavaProject;
import org.eclipse.jdt.internal.core.NameLookup;
import org.eclipse.jdt.internal.core.Openable;
import org.eclipse.jdt.internal.core.OpenableElementInfo;
import org.eclipse.jdt.internal.core.SearchableEnvironment;
import org.eclipse.jdt.internal.core.SelectionRequestor;
import org.eclipse.jdt.internal.core.SourceType;
import org.eclipse.jdt.internal.core.SourceTypeElementInfo;
import org.eclipse.jdt.internal.core.builder.BatchImageBuilder;
import org.eclipse.jdt.internal.core.dom.rewrite.ImportRewriteAnalyzer;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.eclipse.jdt.internal.core.search.matching.JavaSearchNameEnvironment;
import org.eclipse.jdt.internal.core.search.matching.PossibleMatch;
import org.eclipse.jdt.internal.core.CompilationUnitProblemFinder;
import org.eclipse.jdt.internal.core.hierarchy.HierarchyResolver;
import org.eclipse.jdt.internal.core.SourceRefElement;
import org.eclipse.jdt.internal.core.search.JavaSearchParticipant;
import org.eclipse.jdt.core.search.SearchDocument;
import org.eclipse.jdt.core.search.SearchPattern;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchRequestor;
import org.eclipse.jdt.internal.core.search.matching.MatchLocator;
import org.eclipse.jdt.internal.core.search.matching.PossibleMatch;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.search.SearchEngine;
import org.eclipse.jdt.internal.junit.launcher.JUnit4TestFinder;
import java.util.Set;
import org.eclipse.jdt.internal.compiler.parser.RecoveredElement;
import org.eclipse.jdt.internal.compiler.SourceElementNotifier;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
import org.eclipse.jdt.internal.core.search.indexing.IndexingParser;
import org.eclipse.jdt.internal.compiler.ISourceElementRequestor;
import org.eclipse.jdt.internal.core.search.indexing.ReadWriteMonitor;
import org.eclipse.jdt.internal.core.search.indexing.SourceIndexerRequestor;
import org.eclipse.jdt.internal.core.search.indexing.SourceIndexer;
import org.eclipse.jdt.internal.core.search.indexing.AbstractIndexer;
import org.eclipse.jdt.internal.core.index.Index;
import org.eclipse.jdt.internal.compiler.ast.MethodDeclaration;

@SuppressWarnings("restriction")
public aspect ParserHook {

    private static ThreadLocal<LookupEnvironment> lookupEnvironmentOfAskForType = new ThreadLocal<LookupEnvironment>();
    
    private static ThreadLocal<LookupEnvironment> lookupEnvironmentOfGenerateInfos =
    		new ThreadLocal<LookupEnvironment>();

    private static ThreadLocal<LookupEnvironment> lookupEnvironmentOfMatchLocator = new ThreadLocal<LookupEnvironment>();
    
	private static final ReflectField<Parser, Boolean> dietOfParser =
		ReflectField.getField(Parser.class, boolean.class, "diet");
	
	private static final ReflectField<CompletionEngine, IJavaProject> javaProjectOfCompletionEngine =
			ReflectField.getField(CompletionEngine.class, IJavaProject.class, "javaProject"); 	

	private static final ReflectField<CompletionEngine, WorkingCopyOwner> ownerOfCompletionEngine =
			ReflectField.getField(CompletionEngine.class, WorkingCopyOwner.class, "owner"); 	

	private static final ReflectField<SelectionEngine, ISelectionRequestor> requestorOfSelectionEngine =
			ReflectField.getField(SelectionEngine.class, ISelectionRequestor.class, "requestor"); 	

	private static final ReflectField<SelectionRequestor, Openable> openableOfSelectionRequestor =
			ReflectField.getField(SelectionRequestor.class, Openable.class, "openable"); 	

	private static final ResultMethod1Args<PackageBinding, ReferenceBinding, char[]> getType0MethodOfPackageBinding =
			ReflectMethod.method(PackageBinding.class, ReferenceBinding.class, "getType0", char[].class);

	private static final ReflectField<CompilationUnitDeclaration, Integer>
		suppressWarningsCountOfCompilationUnitDeclaration = ReflectField.getField(CompilationUnitDeclaration.class,
				int.class, "suppressWarningsCount");

	private static final ReflectField<CompilationUnitDeclaration, Annotation[]>
		suppressWarningAnnotationsOfCompilationUnitDeclaration = ReflectField.getField(CompilationUnitDeclaration.class,
				Annotation[].class, "suppressWarningAnnotations");

	private static final ReflectField<SearchableEnvironment, JavaProject> projectOfSearchableEnvironment =
			ReflectField.getField(SearchableEnvironment.class, JavaProject.class, "project");

	private static final ReflectField<SearchableEnvironment, WorkingCopyOwner> ownerOfSearchableEnvironment =
			ReflectField.getField(SearchableEnvironment.class, WorkingCopyOwner.class, "owner");
	
	private static final ReflectField<HierarchyResolver, LookupEnvironment> lookupEnvironmentOfHierarchyResolver =
			ReflectField.getField(HierarchyResolver.class, LookupEnvironment.class, "lookupEnvironment");

	private static final ReflectField<SourceElementNotifier, ISourceElementRequestor> requestorOfSourceElementNotifier =
			ReflectField.getField(SourceElementNotifier.class, ISourceElementRequestor.class, "requestor");

	private static final ReflectField<SourceIndexerRequestor, SourceIndexer> indexerOfSourceIndexerRequestor =
			ReflectField.getField(SourceIndexerRequestor.class, SourceIndexer.class, "indexer");

	private static final ReflectField<AbstractIndexer, SearchDocument> documentOfAbstractIndexer =
			ReflectField.getField(AbstractIndexer.class, SearchDocument.class, "document");

	private static final ReflectField<SearchDocument, Index> indexOfSearchDocument =
			ReflectField.getField(SearchDocument.class, Index.class, "index");

	private static final VoidMethod0Args<CompilationUnitScope> checkAndSetImportsMethodOfCompilationUnitScope =
			ReflectMethod.voidMethod(CompilationUnitScope.class, "checkAndSetImports");
	
	
	pointcut parse(Object caller, ICompilationUnit sourceUnit): this(caller)
		&& parseWithoutExclusions(sourceUnit)

		// exclude the control flow of 'Compiler.resolve'; will be handled by the 'compilerResolve' pointcut
		&& !cflow(execution(public CompilationUnitDeclaration Compiler.resolve(
				CompilationUnitDeclaration, ICompilationUnit, boolean, boolean, boolean)))

		// exclude the control flow of 'CompilationUnit.buildStructure'; handled by 'parseInBuildStructureOfCompilationUnit' pointcut
		&& !cflow(execution(protected boolean CompilationUnit.buildStructure(
				OpenableElementInfo, IProgressMonitor, Map, IResource)))

		// exclude the control flow of 'Compiler.compile'; JSR-268 processors will be called later on during compilation
		&& !cflow(execution(public void Compiler.compile(ICompilationUnit[])))

		&& !cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))
		
		&& !cflow(execution(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot)))
		
		&& !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor)));



	pointcut parseWithoutExclusions(ICompilationUnit sourceUnit):
		call(public CompilationUnitDeclaration Parser.parse(ICompilationUnit, CompilationResult))
		&& args(sourceUnit, CompilationResult)
		&& !cflow(execution(public CompilationUnitDeclaration Parser.dietParse(ICompilationUnit, CompilationResult)));



	private pointcut dietParse(Object caller, ICompilationUnit sourceUnit): this(caller)
		&& dietParseWithoutExclusions(sourceUnit)
	
		// ignore from control flow on 'Compiler.resolve'; will be handled by the 'compilerResolve' pointcut
		&& !cflow(execution(public CompilationUnitDeclaration Compiler.resolve(
				CompilationUnitDeclaration, ICompilationUnit, boolean, boolean, boolean)))

		// ignore calls from control flow of 'Compiler.compile'; the processors are already called during compilation
		&& !cflow(execution(public void Compiler.compile(ICompilationUnit[])))

		&& !cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))	

        && !cflow(execution(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot)))

        && !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor)));



	pointcut dietParseWithoutExclusions(ICompilationUnit sourceUnit):
		call(public CompilationUnitDeclaration Parser.dietParse(ICompilationUnit, CompilationResult))
		&& args(sourceUnit, CompilationResult);



	pointcut parseInCompilerResolve(Object caller, ICompilationUnit sourceUnit): this(caller)
		&& (parseWithoutExclusions(sourceUnit) || dietParseWithoutExclusions(sourceUnit))

		&& cflow(execution(public CompilationUnitDeclaration Compiler.resolve(
				CompilationUnitDeclaration, ICompilationUnit, boolean, boolean, boolean)))

		// exclude the control flow of 'CompilationUnit.buildStructure'; handled by 'parseInBuildStructureOfCompilationUnit' pointcut
		&& !cflow(execution(protected boolean CompilationUnit.buildStructure(
				OpenableElementInfo, IProgressMonitor, Map, IResource)))

		&& !cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))	

	    && !cflow(execution(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot)))

	    && !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor)));	


	public LookupEnvironment SourceElementParser.lookupEnvironment = null;


	pointcut setParserOfCompilationUnitProblemFinder(CompilationUnitProblemFinder target, SourceElementParser newValue):
		target(target) && set(Parser Compiler.parser) && args(newValue)
			&& withincode(public static CompilationUnitDeclaration CompilationUnitProblemFinder.process(
					CompilationUnit, SourceElementParser, WorkingCopyOwner, HashMap, boolean, int, IProgressMonitor));


	after(CompilationUnitProblemFinder target, SourceElementParser newValue):
			setParserOfCompilationUnitProblemFinder(target, newValue) {
		if (!(newValue instanceof SourceElementParser)) {
			return;
		}

		try {
			Field field = newValue.getClass().getDeclaredField("lookupEnvironment");
			field.set(newValue, target.lookupEnvironment);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (NoSuchFieldException e) {
			throw new RuntimeException(e);
		}
	}


	pointcut parseInBuildStructureOfCompilationUnit(SourceElementParser caller, ICompilationUnit sourceUnit):
		this(caller) && call(public CompilationUnitDeclaration Parser.parse(ICompilationUnit, CompilationResult))
		&& args(sourceUnit, CompilationResult)
		&& cflow(execution(protected boolean CompilationUnit.buildStructure(
				OpenableElementInfo, IProgressMonitor, Map, IResource)))
		&& !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor)));


	CompilationUnitDeclaration around(SourceElementParser caller, ICompilationUnit sourceUnit):
			parseInBuildStructureOfCompilationUnit(caller, sourceUnit) {
		//TODO: uncomment next line!!!
//				&& !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor))) {
		//TODO: if next line is uncommented 'outline' will not be updated anymore, however the JavaDoc of a invoke on a
		// generated method will be property shown (instead of showing 2 hyperlinks) 
//				&& !cflow(execution(protected void Openable.generateInfos(Object, HashMap, IProgressMonitor))) {
		LookupEnvironment lookupEnvironment;
		try {
			Field field = caller.getClass().getDeclaredField("lookupEnvironment");
			lookupEnvironment = (LookupEnvironment) field.get(caller);
		} catch (SecurityException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		} catch (NoSuchFieldException e) {
			throw new RuntimeException(e);
		}

		CompilationUnitDeclaration parsedUnit;
		
		boolean requiresFullParse =
				Thread.currentThread().getName().equals("org.eclipse.jdt.internal.ui.text.JavaReconciler")
					|| Thread.currentThread().getName().equals("main");
		
//		if (lookupEnvironment != null) {
			try {
				boolean previousValueOfDietField = (Boolean) dietOfParser.get(caller);
				if (requiresFullParse && !AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
					dietOfParser.set(caller, false);
				}

//				caller.methodRecoveryActivated = true;
//				Field statementRecoveryActivatedField =
//						ReflectUtils.getInaccessibleField(Parser.class, "statementRecoveryActivated");
//				ReflectUtils.setFieldValue(statementRecoveryActivatedField, caller, true);
					
				parsedUnit = proceed(caller, sourceUnit);

				if (requiresFullParse && !AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
					dietOfParser.set(caller, previousValueOfDietField);
				} else if (!requiresFullParse) {
					parsedUnit.ignoreMethodBodies = true;
				}
			} catch (IllegalAccessException e) {
				throw new JxtendEclipseHookException(e);
			}
//		} else {
//			parsedUnit = proceed(caller, sourceUnit);
//		}

		boolean completeTypeBindings = true;
		boolean deregisterAtEnd = true;

		if (lookupEnvironment == null) {
			lookupEnvironment = lookupEnvironmentOfAskForType.get();
			
			if (lookupEnvironment == null) {
				lookupEnvironment = lookupEnvironmentOfMatchLocator.get();
//				
//				completeTypeBindings = true;
//				deregisterAtEnd = false;
			}
		}
			
		AnnotationProcessorInvoker.invoke(
				null, sourceUnit, parsedUnit, lookupEnvironment, completeTypeBindings, deregisterAtEnd);

		return parsedUnit;
	}

//	after(CompilationUnitDeclaration[] parsedUnits, HierarchyResolver caller): //, HierarchyResolver caller):
//		withincode(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor))
//			&& call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration[], boolean[], int))
//			&& args(parsedUnits, boolean[], int) && this(caller) {
//		@SuppressWarnings("unused")
//		LookupEnvironment lookupEnvironment =
//				(LookupEnvironment) ReflectUtils.inaccessibleFieldValue(caller, "lookupEnvironment");
//		
////		AnnotationProcessorInvoker.invoke(parser, sourceUnit, parsedUnit, lookupEnvironment);
//		
//		//TODO: continue here later!!! AND UNCOMMENT LINE AFTER "TODO: uncomment next line!!!"
//	}

	after(ICompilationUnit sourceUnit, Object caller) returning (CompilationUnitDeclaration parsedUnit):
			((parse(caller, sourceUnit) || dietParse(caller, sourceUnit))
					&& !cflow(execution(public void Compiler.accept(ICompilationUnit, AccessRestriction)))
				|| parseInCompilerResolve(caller, sourceUnit)) {
		//TODO: uncomment next line!!!
//				&& !cflow(execution(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor))) {

		LookupEnvironment lookupEnvironment =
			(caller instanceof Compiler) ? ((Compiler) caller).lookupEnvironment : null;

		if (!Thread.currentThread().getName().equals("ModalContext")) {
		AnnotationProcessorInvoker.invoke((caller instanceof Compiler) ? ((Compiler) caller).parser : null,
				sourceUnit, parsedUnit,
				lookupEnvironment != null ? lookupEnvironment : lookupEnvironmentOfAskForType.get(), true,
				lookupEnvironmentOfMatchLocator.get() == null);
		}
	}

	void around(Compiler compiler, LookupEnvironment lookupEnvironment, CompilationUnitDeclaration parsedUnit):
		withincode(public void Compiler.accept(ICompilationUnit, AccessRestriction))
			&& this(compiler)
			&& call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration))
			&& target(lookupEnvironment)
			&& args(parsedUnit) {
		lookupEnvironment.completeTypeBindings(parsedUnit, true);

		if (compiler.getClass().getName().equals("org.eclipse.jdt.core.dom.CompilationUnitResolver")) {
			parsedUnit.ignoreMethodBodies = true;
		}

		AnnotationProcessorInvoker.invoke(compiler.parser, parsedUnit.compilationResult.compilationUnit, parsedUnit,
				lookupEnvironment, false, false);
	}
	
	pointcut callSetSourceRange(int startPosition, int length):
		call(public void org.eclipse.jdt.core.dom.ASTNode.setSourceRange(int, int))
			&& args(startPosition, length);

//	void around(org.eclipse.jdt.core.dom.MethodDeclaration target, int startPosition, int length): target(target)
//			&& callSetSourceRange(startPosition, length)
//			&& cflow(execution(public org.eclipse.jdt.core.dom.ASTNode org.eclipse.jdt.core.dom.ASTConverter.convert(
//					AbstractMethodDeclaration))) {
//		if ((startPosition >= 0) && (length < 0)) {
//			length = target.getName().getLength(); 
//		}
//
//		proceed(target, startPosition, length);
//	}

	//TODO: investigate if this hook is still needed?!?
	void around(int startPosition, int length): callSetSourceRange(startPosition, length)
			&& cflow(execution(public * org.eclipse.jdt.core.dom.ASTConverter.convert(ASTNode+))) {
		if ((startPosition >= 0) && (length < 0)) {
			length = 0; 
		}

		proceed(startPosition, length);
	}

	//TODO: investigate if this hook is still needed; why would we like to do nothing in case Parser#restartRecovery is
	//	set to false?!?
	void around(SelectionParser target, boolean restartRecovery): target(target)
			&& set(boolean Parser.restartRecovery) && args(restartRecovery)
			&& cflow(execution(public void AssistParser.parseBlockStatements(AbstractMethodDeclaration, CompilationUnitDeclaration))) {
		if (!restartRecovery) {
			proceed(target, restartRecovery);
		}
	}

	pointcut parseBlockStatementsForTooltips(
			SelectionEngine selectionEngine, CompilationUnitDeclaration unit, int position):
		call(protected ASTNode Engine.parseBlockStatements(CompilationUnitDeclaration, int))
		&& target(selectionEngine) && args(unit, position)
		&& withincode(public void SelectionEngine.select(ICompilationUnit, int, int));

	pointcut faultInTypesForTooltips():
		call(public void CompilationUnitScope.faultInTypes())
		&& withincode(public void SelectionEngine.select(ICompilationUnit, int, int));
//		cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))
//		&& target(unitScope) && execution(public void CompilationUnitScope.faultInTypes());
			
	after(SelectionEngine selectionEngine, CompilationUnitDeclaration unit, int position):
			parseBlockStatementsForTooltips(selectionEngine, unit, position) {
		if (!unit.ignoreMethodBodies) {
			AnnotationProcessorInvoker.invoke(
					null, unit.compilationResult.compilationUnit, unit, selectionEngine.lookupEnvironment);
		}
	}

	// Prevent setting the 'initialization' field from AbstractVariableDeclaration to 'null' in the method
	// 'SelectionParser.consumeExitVariableWithInitialization()'; Normally this field will be cleared by the
	// parser used for showing the tooltip information, however we still need in for our 'val' / 'var' type inference.
	void around(Expression expression): set(Expression AbstractVariableDeclaration.initialization) && args(expression)
			&& withincode(protected void SelectionParser.consumeExitVariableWithInitialization()) {
		if (expression == null) {
			return;
		}

		proceed(expression);
	}

	void around(): execution(public void (ASTNode+).resolve(BlockScope+))
	        && within(org.eclipse.jdt.internal.codeassist.select.*)
			&& cflow(parseBlockStatementsForTooltips(SelectionEngine, CompilationUnitDeclaration, int)) {
		try {
			proceed();
		} catch (SelectionNodeFound e) {
			if (!AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				throw e;
			}
		}
	}

	pointcut resolveTypeWithBlockScopeForTooltips(Scope blockScope):
		within(org.eclipse.jdt.internal.codeassist.select.*)
				&& execution(public TypeBinding (ASTNode+).resolveType(BlockScope)) && args(blockScope);

	pointcut resolveTypeForTooltips(Scope scope): resolveTypeWithBlockScopeForTooltips(scope) ||
		((execution(public TypeBinding	SelectionOnParameterizedSingleTypeReference.resolveType(BlockScope, boolean))
				|| execution(public TypeBinding SelectionOnParameterizedSingleTypeReference.resolveType(ClassScope))
				|| execution(public TypeBinding	SelectionOnParameterizedQualifiedTypeReference.resolveType(
								BlockScope, boolean))
				|| execution(public TypeBinding SelectionOnParameterizedQualifiedTypeReference.resolveType(
								ClassScope))
				|| execution(protected TypeBinding SelectionOnSingleTypeReference.getTypeBinding(Scope))
				|| execution(protected TypeBinding SelectionOnQualifiedTypeReference.getTypeBinding(Scope))
				|| execution(public TypeBinding SelectionOnQualifiedAllocationExpression.resolveType(BlockScope)))
						&& args(scope));

	TypeBinding around(Scope scope): resolveTypeForTooltips(scope)
			&& cflow(parseBlockStatementsForTooltips(SelectionEngine, CompilationUnitDeclaration, int)) {
		try {
			return proceed(scope);
		} catch (SelectionNodeFound e) {
			if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				return (e.binding instanceof TypeBinding) ? (TypeBinding) e.binding : null;
			} else {
				throw e;
			}
		}		
	}

	TypeBinding around(Scope scope): resolveTypeForTooltips(scope)
			&& cflow(faultInTypesForTooltips()) {
		try {
			return proceed(scope);
		} catch (SelectionNodeFound e) {
			if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				return (e.binding instanceof TypeBinding) ? (TypeBinding) e.binding : null;
			} else {
				throw e;
			}
		}		
	}

	private TypeBinding handleSelectionNodeFoundForFaultInType(
			Scope scope, SelectionNodeFound e, ASTNode node, boolean throwEx) {
		if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
			return (e.binding instanceof TypeBinding) ? (TypeBinding) e.binding : null;
		} else {
			Scope currentScope = scope;
			while (!(currentScope instanceof CompilationUnitScope)) {
				currentScope = currentScope.parent;
			}

			assert currentScope instanceof CompilationUnitScope;

			CompilationUnitScope unitScope = (CompilationUnitScope) currentScope;

			AnnotationProcessorInvoker.invoke(null, unitScope.referenceContext.compilationResult.compilationUnit,
					unitScope.referenceContext, lookupEnvironmentOfAskForType.get());
			
			unitScope.referenceContext.ignoreMethodBodies = true;

//			if (e.binding != null) {
//				throw new SelectionNodeFound(e.binding);
//			} else {
//				return null;
//			}
//			throw e.binding;

			Binding binding = e.binding;
			if ((binding == null) && (node != null)) {
				binding =
					ReflectField.getPublicField((Class<ASTNode>) node.getClass(), Binding.class, "binding").get(node);
			}

			if (throwEx) {
				throw new SelectionNodeFound(binding);
			}

			return (binding instanceof TypeBinding) ? (TypeBinding) binding : null;
		}
	}
	
//	TypeBinding around(Scope scope): resolveTypeWithBlockScopeForTooltips(scope)
//			&& cflow(faultInTypesForTooltips()) {
//		try {
//			return proceed(scope);
//		} catch (SelectionNodeFound e) {
//			return handleSelectionNodeFoundForFaultInType(scope, e, (ASTNode) null, false);
//		}
//	}

	void around(SelectionOnNameOfMemberValuePair selectionOnNameOfMemberValuePair, Scope scope):
			execution(public void SelectionOnNameOfMemberValuePair.resolveTypeExpecting(BlockScope, TypeBinding))
				&& args(scope, TypeBinding) && this(selectionOnNameOfMemberValuePair)
				&& cflow(faultInTypesForTooltips()) {
		try {
			proceed(selectionOnNameOfMemberValuePair, scope);
		} catch (SelectionNodeFound e) {
			handleSelectionNodeFoundForFaultInType(scope, e, selectionOnNameOfMemberValuePair, true);
		}
	}

//	void around(Scope scope): execution(public void SelectionOnNameOfMemberValuePair.resolveTypeExpecting(
//				BlockScope, TypeBinding)) && args(scope, TypeBinding) {
////			&& cflow(parseBlockStatementsForTooltips(SelectionEngine, CompilationUnitDeclaration, int)) {
//		try {
//			proceed(scope);
//		} catch (SelectionNodeFound e) {
//			if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
//				return;
//			} else {
//				throw e;
//			}
//		}		
//	}

	//TODO: investigate if this hook is still needed; why would we like to do nothing in case Parser#restartRecovery is
	//	set to false?!?
	void around(CompletionParser target, boolean restartRecovery): target(target)
			&& set(boolean Parser.restartRecovery) && args(restartRecovery)
			&& cflow(execution(public void AssistParser.parseBlockStatements(
					AbstractMethodDeclaration, CompilationUnitDeclaration))) {
		if (!restartRecovery) {
			proceed(target, restartRecovery);
		}
	}

	pointcut parseBlockStatementsForCodeCompletion(
			CompletionEngine completionEngine, CompilationUnitDeclaration unit, int position):
		call(protected ASTNode Engine.parseBlockStatements(CompilationUnitDeclaration, int))
			&& target(completionEngine) && args(unit, position)
			&& withincode(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot));

	pointcut faultInTypesForCodeCompletion():
		call(public void CompilationUnitScope.faultInTypes())
		&& withincode(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot));

	//TODO: use the same construction for 'SelectionOn...' types?!?
	after(CompletionEngine completionEngine, CompilationUnitDeclaration unit, int position):
			parseBlockStatementsForCodeCompletion(completionEngine, unit, position) {
		AnnotationProcessorInvoker.invoke(
				null, unit.compilationResult.compilationUnit, unit, completionEngine.lookupEnvironment);

//		unit.scope.faultInTypes();
	}

	void around(): execution(public void (ASTNode+).resolve(BlockScope+))
    		&& within(org.eclipse.jdt.internal.codeassist.complete.*)
    		&& cflow(parseBlockStatementsForCodeCompletion(CompletionEngine, CompilationUnitDeclaration, int)) {
		try {
			proceed();
		} catch (CompletionNodeFound e) {
			if (!AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				throw e;
			}
		}
	}

//	void around(MessageSend messageSend):
//			cflow(execution(public TypeBinding MessageSend.resolveType(BlockScope)))
//			&& call(public void ProblemReporter.invalidMethod(MessageSend, MethodBinding))
//			&& args(messageSend, MethodBinding) {
//		if (messageSend.binding != null && !messageSend.binding.isValidBinding()
//				&& messageSend.binding.declaringClass != null
//				&& !messageSend.binding.declaringClass.isAnnotationType()
//
//				&& messageSend.binding.declaringClass.superclass() instanceof BinaryTypeBinding) {
//			return;
//		}
//
//		proceed(messageSend);
//	}

	before(FieldDeclaration fieldDeclaration): execution(public void FieldDeclaration.resolve(MethodScope))
			&& this(fieldDeclaration) {
//			&& cflow(execution(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot))) {
		if (fieldDeclaration.binding == null) {
			return;
		}

		fieldDeclaration.binding.declaringClass.getField(fieldDeclaration.name, true);
	}

	pointcut resolveTypeWithBlockScopeForCodeCompletion(ASTNode self, Scope blockScope):
		within(org.eclipse.jdt.internal.codeassist.complete.*)
			&& execution(public TypeBinding (ASTNode+).resolveType(BlockScope)) && args(blockScope) && this(self);

	TypeBinding around(ASTNode self, Scope scope): (resolveTypeWithBlockScopeForCodeCompletion(self, scope)

						//TODO: Add more '|| execution(..)' lines for each 'CompletionOn..' class
						|| (execution(protected TypeBinding CompletionOnSingleTypeReference.getTypeBinding(Scope)))
								&& args(scope)
								&& this(self))
//					|| execution(public TypeBinding CompletionOnSingleNameReference.resolveType(BlockScope)))

				&& cflow(parseBlockStatementsForCodeCompletion(CompletionEngine, CompilationUnitDeclaration, int)) {
		try {
			return proceed(self, scope);
		} catch (CompletionNodeFound e) {
			if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				return (e.qualifiedBinding instanceof TypeBinding) ? (TypeBinding) e.qualifiedBinding : null;
			} else {
				throw e;
			}
		}
	}

	TypeBinding around(ASTNode self, Scope scope): resolveTypeWithBlockScopeForCodeCompletion(self, scope)
			&& cflow(faultInTypesForCodeCompletion()) {
		try {
			return proceed(self, scope);
		} catch (CompletionNodeFound e) {
			if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
				return (e.qualifiedBinding instanceof TypeBinding) ? (TypeBinding) e.qualifiedBinding : null;
			} else {
				Scope currentScope = scope;
				while (!(currentScope instanceof CompilationUnitScope)) {
					currentScope = currentScope.parent;
				}
	
				assert currentScope instanceof CompilationUnitScope;
	
				CompilationUnitScope unitScope = (CompilationUnitScope) currentScope;
	
				AnnotationProcessorInvoker.invoke(null, unitScope.referenceContext.compilationResult.compilationUnit,
						unitScope.referenceContext, lookupEnvironmentOfAskForType.get());
	
				unitScope.referenceContext.ignoreMethodBodies = true;

				if (self instanceof CompletionOnQualifiedNameReference) {
					throw e;
				}

				return (e.qualifiedBinding instanceof TypeBinding) ? (TypeBinding) e.qualifiedBinding : null;
			}
		}
	}


	// Prevent setting the 'initialization' field from AbstractVariableDeclaration to null in the method
	// 'CompletionParser.consumeExitVariableWithInitialization()'; Normally this field will be cleared by the
	// parser used for 'code completion', however we still need in for our 'val' / 'var' type inference.
	void around(Expression expression): set(Expression AbstractVariableDeclaration.initialization) && args(expression)
			&& withincode(protected void CompletionParser.consumeExitVariableWithInitialization()) {
		if (expression == null) {
			return;
		}

		proceed(expression);
	}

	//TODO: Test if we still need this specific hook
	int around(Compiler caller): get(int Compiler.parseThreshold) && this(caller)
			&& withincode(protected void Compiler.internalBeginToCompile(ICompilationUnit[], int)) {
		if (caller instanceof CompilationUnitProblemFinder) {
			return Integer.MAX_VALUE;
		} else {
			return proceed(caller);
		}
	}

	//TODO: Test if we still need this specific hook	
	int around(Compiler caller): get(int Compiler.parseThreshold) && this(caller)
			&& withincode(protected void Compiler.internalBeginToCompile(ICompilationUnit[], int)) {
		if (caller instanceof CompilationUnitProblemFinder) {
			return Integer.MAX_VALUE;
		} else {
			return proceed(caller);
		}
	}

	void around(SourceElementParser caller): set(boolean Parser.diet)
			&& this(caller) && withincode(protected boolean Parser.resumeAfterRecovery()) {
		Field field = ReflectUtils.getPublicField(SourceElementParser.class, "lookupEnvironment");
		LookupEnvironment lookupEnvironment = (LookupEnvironment) ReflectUtils.fieldValue(field, caller);

		if (lookupEnvironment != null) {
			// do nothing
			return;
		}

		proceed(caller);
	}

	pointcut faultInTypesInSelectionEngineSelect(CompilationUnitScope unitScope):
			cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))
				&& target(unitScope) && execution(public void CompilationUnitScope.faultInTypes());

	//TODO: when this in 'undocumented' the tooltip 'is' shown on 'readOnly' of @property, but the tooltip is no longer
	//	shown on the 2nd or later @property occurences
	void around(CompilationUnitScope unitScope): faultInTypesInSelectionEngineSelect(unitScope) {
//		cflow(execution(public void SelectionEngine.select(ICompilationUnit, int, int)))
//			&& target(unitScope) && execution(public void CompilationUnitScope.faultInTypes()) {
		try {
			proceed(unitScope);
		} catch (SelectionNodeFound e) {
			AnnotationProcessorInvoker.invoke(null, unitScope.referenceContext.compilationResult.compilationUnit,
					unitScope.referenceContext, lookupEnvironmentOfAskForType.get());

			unitScope.referenceContext.ignoreMethodBodies = true;
		}
	}

	boolean around(org.eclipse.jdt.core.dom.SimpleName simpleName):
			execution(public boolean org.eclipse.jdt.internal.ui.javaeditor.SemanticHighlightingReconciler.PositionCollector.visit(
					org.eclipse.jdt.core.dom.SimpleName))
				&& args(simpleName) {
		if ((simpleName.getFlags() & ASTNodeTypeAndFlags.JXTEND_GENERATED) == 0) {
			return proceed(simpleName);			
		}

		return false;
	}

	after(ASTNode astNode) returning (org.eclipse.jdt.core.dom.ASTNode result):
			execution(public (org.eclipse.jdt.core.dom.*) (org.eclipse.jdt.core.dom.ASTConverter).convert(
					org.eclipse.jdt.internal.compiler.ast.*)) && args(astNode) {
		if ((astNode.bits & ASTNodeBits.JXTEND_GENERATED) != 0) {
			List<org.eclipse.jdt.core.dom.ASTNode> astNodes = new ArrayList<org.eclipse.jdt.core.dom.ASTNode>();

			ASTVisitor astVisitor = MarkJxtendGeneratedASTVisitorMethodInterceptor.createASTVisitor(astNodes);
			result.accept(astVisitor);

			for (org.eclipse.jdt.core.dom.ASTNode currentNode : astNodes) {
				currentNode.setFlags(ASTNodeTypeAndFlags.JXTEND_GENERATED); // | org.eclipse.jdt.core.dom.ASTNode.PROTECT);
			}
		}
	}

    after(ImportReference importReference) returning (org.eclipse.jdt.core.dom.ImportDeclaration result):
		execution(public (org.eclipse.jdt.core.dom.ImportDeclaration)
					(org.eclipse.jdt.core.dom.ASTConverter).convertImport(ImportReference)) && args(importReference) {
		if ((importReference.bits & ASTNodeBits.JXTEND_GENERATED) != 0) {
			result.setFlags(ASTNodeTypeAndFlags.JXTEND_GENERATED);
		}
    }
	
	void around(org.eclipse.jdt.core.dom.ASTNode target, int typeAndFlags):
			target(target) && set(int org.eclipse.jdt.core.dom.ASTNode.typeAndFlags) && args(typeAndFlags) {
		if ((typeAndFlags & ASTNodeTypeAndFlags.JXTEND_GENERATED) != 0) {
			typeAndFlags |= ASTNodeTypeAndFlags.JXTEND_GENERATED;
		}

		proceed(target, typeAndFlags);
	}

	//Issue #11; Replaces the 'lookupEnvironment.completeTypeBindings(parsedUnit)' call in the (overridden) method
	// 'CompilationUnitProblemFinder#accept' with a call 'lookupEnvironment.completeTypeBindings(parsedUnit, true)'
//	void around(LookupEnvironment lookupEnvironment, CompilationUnitDeclaration parsedUnit):
//		call(void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration))
//		    && target(lookupEnvironment) && args(parsedUnit)
//			&& withincode(public void CompilationUnitProblemFinder.accept(
//				ISourceType[], PackageBinding, AccessRestriction)) {
//		lookupEnvironment.completeTypeBindings(parsedUnit, true);
//
////		AnnotationProcessorInvoker.invoke(parser, sourceUnit, parsedUnit, lookupEnvironment, false, false);
//	}

	void around(LookupEnvironment lookupEnvironment, CompilationUnitDeclaration parsedUnit):
//		call(public void LookupEnvironment.buildTypeBindings(CompilationUnitDeclaration, AccessRestriction))
		call(public void  LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration))
	    	&& target(lookupEnvironment) && args(parsedUnit)
	    	&& withincode(public void CompilationUnitProblemFinder.accept(
	    			ISourceType[], PackageBinding, AccessRestriction)) {

		int secondSlashIndex = CharOperation.indexOf('/', parsedUnit.getFileName(), 1);
		String javaProjectName = new String(CharOperation.subarray(parsedUnit.getFileName(), 1, secondSlashIndex));
		JavaProject javaProject =
				(JavaProject) JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(javaProjectName);

//		Field stepCompletedField = ReflectUtils.getInaccessibleField(LookupEnvironment.class, "stepCompleted");
//		if (((Integer) ReflectUtils.fieldValue(stepCompletedField, lookupEnvironment)).intValue() == 4) {
//			ReflectUtils.setFieldValue(stepCompletedField, lookupEnvironment, 3);
//		}

		lookupEnvironment.completeTypeBindings(parsedUnit, true);

		AnnotationProcessorInvoker.invoke(
				null, javaProject, DefaultWorkingCopyOwner.PRIMARY, parsedUnit, lookupEnvironment, false, false);
	}

    List around(): //org.eclipse.jdt.core.dom.CompilationUnit root):
    	execution(List (org.eclipse.jdt.core.dom.CompilationUnit).imports()) //&& target(root)
    		&& cflow(execution(private IRegion ImportRewriteAnalyzer.evaluateReplaceRange(
    				org.eclipse.jdt.core.dom.CompilationUnit))) {
    	System.out.println("!!! YOYO");
    	List<ImportDeclaration> result = proceed(); //root);
    	return result;
	}

//    List around(): call(public List (org.eclipse.jdt.core.dom.CompilationUnit).imports()) {
////    		&& withincode(private IRegion ImportRewriteAnalyzer.evaluateReplaceRange(
////    				org.eclipse.jdt.core.dom.CompilationUnit)) {
//    	System.out.println("!!2 YOYO");
//    	List imports = proceed();
//    	if (!(thisJoinPoint.getThis() instanceof ImportRewriteAnalyzer)) {
//    		return imports;
//    	}
//
//		List filterdImports = new ArrayList(imports.size());
//		for (Iterator iterator = imports.iterator(); iterator.hasNext();) {
//			ImportDeclaration importDeclaration = (ImportDeclaration) iterator.next();
//
//			ReflectField<org.eclipse.jdt.core.dom.ASTNode, Integer> typeAndFlagsField =
//				ReflectField.getField(org.eclipse.jdt.core.dom.ASTNode.class, Integer.TYPE, "typeAndFlags");
//			if ((typeAndFlagsField.get(importDeclaration) & ASTNodeTypeAndFlags.JXTEND_GENERATED) == 0) {
//				filterdImports.add(importDeclaration);
//			}
//		}
//		return filterdImports;
//    }

    List around(): call(public List (org.eclipse.jdt.core.dom.CompilationUnit).imports())
    		&& this(ImportRewriteAnalyzer) {
		List imports = proceed();
	
		List filterdImports = new ArrayList(imports.size());
		for (Iterator iterator = imports.iterator(); iterator.hasNext();) {
			ImportDeclaration importDeclaration = (ImportDeclaration) iterator.next();
	
			ReflectField<org.eclipse.jdt.core.dom.ASTNode, Integer> typeAndFlagsField =
				ReflectField.getField(org.eclipse.jdt.core.dom.ASTNode.class, Integer.TYPE, "typeAndFlags");
			if (((typeAndFlagsField.get(importDeclaration) & ASTNodeTypeAndFlags.JXTEND_GENERATED) == 0)) {
//					|| importDeclaration.getName().getFullyQualifiedName().equals("jxtend.runtime.internal.$sam")) {
				filterdImports.add(importDeclaration);
			}
		}
		return filterdImports;
    }

	void around(TypeDeclaration type):
			execution(private void CompilationUnitDeclaration.cleanUp(TypeDeclaration)) && args(type) {
		ClassScope scope = null;
		if (type.binding != null) {
			scope = type.binding.scope;
		}

		proceed(type);

		if (type.binding != null) {
			type.binding.scope = scope;
		}
	}

	ReferenceBinding[] around(SourceTypeBinding target): get(ReferenceBinding[] SourceTypeBinding.superInterfaces)
			&& this(target)
			&& withincode(public MethodBinding SourceTypeBinding.getExactMethod(
					char[], TypeBinding[], CompilationUnitScope)) {
		if (target.superInterfaces != null) {
			return proceed(target);
		}

		return Binding.NO_SUPERINTERFACES;
	}

	ReferenceBinding[] around(): execution(public ReferenceBinding[] (ReferenceBinding+).superInterfaces()) {
		ReferenceBinding[] result = proceed();
		if (result != null) {
			return result;
		}

		return Binding.NO_SUPERINTERFACES;
	}

	Expression around(LocalDeclaration target): get(Expression AbstractVariableDeclaration.initialization)
			&& target(target)
    		&& withincode(private void CompletionEngine.findVariablesAndMethods(
    				char[], Scope, InvocationSite, Scope, boolean, boolean)) {
		if (target.initialization == null || target.binding == null) {
			return proceed(target);
		}

		return null;
	}

	
//	after(Compiler caller, CompilationUnitDeclaration parsedUnit):
//		withincode(public void Compiler.accept(ICompilationUnit, AccessRestriction))
//			&& this(caller)
//			&& call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration))
//			&& args(parsedUnit) {
//		if (!caller.getClass().getName().equals("org.eclipse.jdt.core.dom.CompilationUnitResolver")) {
//			return;
//		}
//
//		AnnotationProcessorInvoker.invoke(caller.parser,
//				parsedUnit.compilationResult.compilationUnit, parsedUnit, caller.lookupEnvironment, false, true);
//	}


//	after(CompletionEngine caller, LookupEnvironment providedLookupEnvironment, CompilationUnitDeclaration parsedUnit): // returning (CompilationUnitDeclaration parsedUnit):
////		call(public static CompilationUnitDeclaration SourceTypeConverter.buildCompilationUnit(
////				ISourceType[], int, ProblemReporter, CompilationResult))
//		call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration, boolean))
//			&& args(parsedUnit, boolean) && target(providedLookupEnvironment)
//			&& withincode(public void Engine.accept(ISourceType[], PackageBinding, AccessRestriction))
//			&& this(caller) {
//
////		CompilationUnitDeclaration parsedUnit = proceed(caller);
//
//		char[] parsedUnitFilename = parsedUnit.getFileName();
//		assert parsedUnitFilename.length > 0;
//
//		LookupEnvironment lookupEnvironment = providedLookupEnvironment;
//
//		JavaProject javaProjectFromEngine = (JavaProject) javaProjectOfCompletionEngine.get(caller); 
//		JavaProject javaProject = javaProjectFromEngine;
//		NameLookup nameLookup = ((SearchableEnvironment) lookupEnvironment.nameEnvironment).nameLookup;
//
//		if (parsedUnitFilename[0] == '/') {
//			int secondSlashIndex = CharOperation.indexOf('/', parsedUnitFilename, 1);
//			assert secondSlashIndex != -1;
//
//			String javaProjectName = new String(CharOperation.subarray(parsedUnitFilename, 1, secondSlashIndex));
//			if (!javaProjectName.equals(javaProjectFromEngine.getProject().getName())) {
//				javaProject = (JavaProject) JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(
//						javaProjectName);
//
//				projectOfSearchableEnvironment.set((SearchableEnvironment) lookupEnvironment.nameEnvironment, javaProject);
////				((SearchableEnvironment) lookupEnvironment.nameEnvironment).nameLookup =
////						javaProject.newNameLookup(javaProject.getO);
////				lookupEnvironment = null;
//			}
//		}
//		
//		
//		AnnotationProcessorInvoker.invoke(caller.getParser(),
//				projectOfSearchableEnvironment.get((SearchableEnvironment) lookupEnvironment.nameEnvironment),
//				ownerOfCompletionEngine.get(caller),
//				parsedUnit, lookupEnvironment, false, false);
//
//		if (javaProject != javaProjectFromEngine) {
//			projectOfSearchableEnvironment.set(
//					(SearchableEnvironment) lookupEnvironment.nameEnvironment, javaProjectFromEngine);
//		}
////		return null;
//	}

	after(CompletionEngine completionEngine, ISourceType[] sourceTypes, PackageBinding packageBinding):
		execution(public void Engine.accept(ISourceType[], PackageBinding, AccessRestriction))
			&& args(sourceTypes, packageBinding, AccessRestriction)
			&& this(completionEngine) {
		
		handleAfterITypeRequestorAccept(completionEngine, sourceTypes, packageBinding);
	}

	after(SelectionEngine selectionEngine, ISourceType[] sourceTypes, PackageBinding packageBinding):
		execution(public void Engine.accept(ISourceType[], PackageBinding, AccessRestriction))
			&& args(sourceTypes, packageBinding, AccessRestriction)
			&& this(selectionEngine) {
		
		handleAfterITypeRequestorAccept(selectionEngine, sourceTypes, packageBinding);
	}	

//	after(SelectionEngine caller, LookupEnvironment lookupEnvironment, CompilationUnitDeclaration parsedUnit): // returning (CompilationUnitDeclaration parsedUnit):
////		call(public static CompilationUnitDeclaration SourceTypeConverter.buildCompilationUnit(
////				ISourceType[], int, ProblemReporter, CompilationResult))
//		call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration, boolean))
//			&& args(parsedUnit, boolean) && target(lookupEnvironment)
//			&& withincode(public void Engine.accept(ISourceType[], PackageBinding, AccessRestriction))
//			&& this(caller) {
//
////		CompilationUnitDeclaration parsedUnit = proceed(caller);
//
//		SelectionRequestor selectionRequestor = (SelectionRequestor) requestorOfSelectionEngine.get(caller);
//		
//		CompilationUnit compilationUnit = (CompilationUnit) openableOfSelectionRequestor.get(selectionRequestor);
//
//		AnnotationProcessorInvoker.invoke(
//				caller.getParser(), compilationUnit, parsedUnit, caller.lookupEnvironment, false, false);
//		
////		return null;
//	}

//	void around(): call(public void CompilationUnitScope.faultInTypes())
//			&& withincode(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot)) {
//		System.out.println();
//	}

	after(CompilationUnitProblemFinder compilationUnitProblemFinder,
			ISourceType[] sourceTypes, PackageBinding packageBinding):
		execution(public void CompilationUnitProblemFinder.accept(ISourceType[], PackageBinding, AccessRestriction))
			&& args(sourceTypes, packageBinding, AccessRestriction)
			&& this(compilationUnitProblemFinder) {
		handleAfterITypeRequestorAccept(compilationUnitProblemFinder, sourceTypes, packageBinding);
	}

	private static void handleAfterITypeRequestorAccept(
			ITypeRequestor typeRequestor, ISourceType[] sourceTypes, PackageBinding packageBinding) {
		LookupEnvironment existingLookupEnvironment;
		if (typeRequestor instanceof CompletionEngine) {
			existingLookupEnvironment = ((CompletionEngine) typeRequestor).lookupEnvironment;
		} else if (typeRequestor instanceof SelectionEngine) {
			existingLookupEnvironment = ((SelectionEngine) typeRequestor).lookupEnvironment;
		} else if (typeRequestor instanceof CompilationUnitProblemFinder) {
			existingLookupEnvironment = ((CompilationUnitProblemFinder) typeRequestor).lookupEnvironment;
		} else {
			throw new IllegalArgumentException();
		}

		ReferenceBinding referenceBinding =
				getType0MethodOfPackageBinding.invoke(packageBinding, sourceTypes[0].getName());

		if (!(referenceBinding instanceof SourceTypeBinding)) {
			throw new IllegalArgumentException();
		}

		final IType sourceTypeHandle = ((SourceTypeElementInfo) sourceTypes[0]).getHandle();
		final JavaProject javaProject = (JavaProject) sourceTypeHandle.getJavaProject();
		final CompilationUnit compilationUnit = (CompilationUnit) sourceTypeHandle.getCompilationUnit();

		final NameLookup originalNameLookup = ((SearchableEnvironment) existingLookupEnvironment.nameEnvironment).nameLookup;

		final SearchableEnvironment searchableEnvironment = (SearchableEnvironment) existingLookupEnvironment.nameEnvironment;
		final JavaProject javaProjectOfSearchableEnvironment =
				projectOfSearchableEnvironment.get((SearchableEnvironment) existingLookupEnvironment.nameEnvironment);

		CompilationUnitDeclaration compilationUnitDeclaration =
				((SourceTypeBinding) referenceBinding).scope.referenceCompilationUnit();
		boolean originalIgnoreMethodBodies = compilationUnitDeclaration.ignoreMethodBodies;
		compilationUnitDeclaration.ignoreMethodBodies = true;

		try {
			LookupEnvironment lookupEnvironment = existingLookupEnvironment;
//			if (javaProject != javaProjectOfSearchableEnvironment) {
//				try {
//					searchableEnvironment.nameLookup = javaProject.newNameLookup(compilationUnit.owner);
//				} catch (JavaModelException e) {
//					throw new RuntimeException(e);
//				}
//				projectOfSearchableEnvironment.set(searchableEnvironment, javaProject);
//				lookupEnvironment = null;
//			}

			// the compilationUnitDeclaration does have bindings and scopes but somehow the "imports" of the
			// CompilationUnitScope is null; to compensate will invoke "checkAndSetImports()" on CompilationUnitScope
			checkAndSetImportsMethodOfCompilationUnitScope.invoke(compilationUnitDeclaration.scope);

			if (javaProject != javaProjectOfSearchableEnvironment) {
				EclipseHookUtils.clearMissingKeywordAnnotationTypes(existingLookupEnvironment);
			}

			AnnotationProcessorInvoker.invoke(
					null, compilationUnit, compilationUnitDeclaration, existingLookupEnvironment, false, false);
		} finally {
			compilationUnitDeclaration.ignoreMethodBodies = originalIgnoreMethodBodies;

//			if (javaProject != javaProjectOfSearchableEnvironment) {
//				projectOfSearchableEnvironment.set(searchableEnvironment, javaProjectOfSearchableEnvironment);
//				((SearchableEnvironment) existingLookupEnvironment.nameEnvironment).nameLookup = originalNameLookup;
//			}
		}
	}
	
//	after(CompletionEngine caller, LookupEnvironment lookupEnvironment, CompilationUnitDeclaration parsedUnit): // returning (CompilationUnitDeclaration parsedUnit):
////		call(public static CompilationUnitDeclaration SourceTypeConverter.buildCompilationUnit(
////				ISourceType[], int, ProblemReporter, CompilationResult))
//		call(public void LookupEnvironment.completeTypeBindings(CompilationUnitDeclaration, boolean))
//			&& args(parsedUnit, boolean) && target(lookupEnvironment)
//			&& withincode(public void CompletionEngine.complete(ICompilationUnit, int, int, ITypeRoot))
//			&& this(caller) {
//
////		CompilationUnitDeclaration parsedUnit = proceed(caller);
//
//		AnnotationProcessorInvoker.invoke(caller.getParser(),
//				(JavaProject) javaProjectOfCompletionEngine.get(caller), ownerOfCompletionEngine.get(caller),
//				parsedUnit, caller.lookupEnvironment, false, false);
//		
////		return null;
//	}
	

	ReferenceBinding around(LookupEnvironment lookupEnvironment):
			execution(ReferenceBinding LookupEnvironment.askForType(PackageBinding, char[]))
					&& this(lookupEnvironment) {
		lookupEnvironmentOfAskForType.set(lookupEnvironment);

		try {
			return proceed(lookupEnvironment);
		} finally {
			lookupEnvironmentOfAskForType.set(null);
		}
	}

	void around(ITypeRequestor typeRequestor, ISourceType[] sourceType, PackageBinding packageBinding, AccessRestriction accessRestriction): //ITypeRequestor typeRequestor, ISourceType[] sourceType, PackageBinding packageBinding, AccessRestriction accessRestriction):
		withincode(ReferenceBinding LookupEnvironment.askForType(PackageBinding, char[]))
			&& call(public void ITypeRequestor.accept(ISourceType[], PackageBinding, AccessRestriction))
			&& args(sourceType, packageBinding, accessRestriction)
			&& target(typeRequestor) {
//		if (!typeRequestor.getClass().getName().equals("org.eclipse.jdt.core.dom.CompilationUnitResolver")) {
//			proceed(typeRequestor, sourceType, packageBinding, accessRestriction);
//			return;
//		}

		char[] name = sourceType[0].getName();
		ReferenceBinding existingBinding = getType0MethodOfPackageBinding.invoke(packageBinding, name);
		if (existingBinding == null) {
			proceed(typeRequestor, sourceType, packageBinding, accessRestriction);
			return;
		}
	}	

	after(CompilationUnitDeclaration compilationUnitDeclaration):
			execution(public void CompilationUnitDeclaration.cleanUp()) && this(compilationUnitDeclaration) {		
		suppressWarningAnnotationsOfCompilationUnitDeclaration.set(compilationUnitDeclaration, new Annotation[3]);
		suppressWarningsCountOfCompilationUnitDeclaration.set(compilationUnitDeclaration, 0);
	}

//	after(HierarchyResolver hierarchyResolver, CompilationUnitDeclaration[] parsedUnits):
//		withincode(public void HierarchyResolver.resolve(Openable[], HashSet, IProgressMonitor))
//			&& call(public void completeTypeBindings(CompilationUnitDeclaration[], boolean[], int))
//			&& args(parsedUnits, boolean[], int)
//			&& this(hierarchyResolver) {
//
//		LookupEnvironment lookupEnvironment = lookupEnvironmentOfHierarchyResolver.get(hierarchyResolver);
//
//		final SearchableEnvironment searchableEnvironment = (SearchableEnvironment) lookupEnvironment.nameEnvironment;
//		final JavaProject javaProjectOfSearchableEnvironment =
//				projectOfSearchableEnvironment.get((SearchableEnvironment) lookupEnvironment.nameEnvironment);
//
//		for (CompilationUnitDeclaration parsedUnit : parsedUnits) {
//			AnnotationProcessorInvoker.invoke(null, javaProjectOfSearchableEnvironment,
//					ownerOfSearchableEnvironment.get(searchableEnvironment), parsedUnit, lookupEnvironment,
//					true, true);
//		}
//
////		for (CompilationUnitDeclaration parsetUnit : parsedUnits) {
////			EclipseProcessorUtils.deregisterTypeDeclarations(parsetUnit, true);
////		}
//	}


	after() returning (SourceTypeBinding sourceTypeBinding):
			execution(SourceTypeBinding ClassScope.buildType(SourceTypeBinding, PackageBinding, AccessRestriction)) {
		if (!AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
			return;
		}

		AnnotationProcessorInvoker.addDelayedCompilationUnitDeregistration(
				sourceTypeBinding.scope.referenceCompilationUnit());
	}

//	void around(Openable openable):
//		withincode(protected void SourceRefElement.generateInfos(Object, HashMap, IProgressMonitor))
//			&& call (protected void Openable.generateInfos(Object, HashMap, IProgressMonitor))
//		    && target(openable) {
//		try {
//			lookupEnvironmentOfGenerateInfos.set(
//					AnnotationProcessorInvoker.createLookupEnvironment((JavaProject) openable.getJavaProject(), DefaultWorkingCopyOwner.PRIMARY));
//
//			proceed(openable);
//		} finally {
//			lookupEnvironmentOfGenerateInfos.set(null);
//		}
//	}

//	void around(JavaProject javaProject):
//		execution(protected void MatchLocator.locateMatches(JavaProject, PossibleMatch[], int, int))
//			&& args(javaProject, PossibleMatch[], int, int) {
//		LookupEnvironment lookupEnvironment =
//				AnnotationProcessorInvoker.createLookupEnvironment(javaProject, DefaultWorkingCopyOwner.PRIMARY);
//
//		lookupEnvironmentOfMatchLocator.set(lookupEnvironment);
//		
//		try {
//			proceed(javaProject);
//		} finally {
//			lookupEnvironmentOfMatchLocator.set(null);
//		}
//	}

	void around(IJavaElement element):
		execution(public void JUnit4TestFinder.findTestsInContainer(IJavaElement, Set, IProgressMonitor))
			&& args(element, Set, IProgressMonitor) {
		JavaProject javaProject = (JavaProject) element.getJavaProject();
		
		LookupEnvironment lookupEnvironment =
				AnnotationProcessorInvoker.createLookupEnvironment(javaProject, DefaultWorkingCopyOwner.PRIMARY);

		lookupEnvironmentOfMatchLocator.set(lookupEnvironment);
		
		try {
			proceed(element);
		} finally {
			lookupEnvironmentOfMatchLocator.set(null);
		}
	}

	void around(): execution(
				private void CompletionEngine.computeExpectedTypesForAllocationExpression(
						ReferenceBinding, Expression[], Scope, InvocationSite)) {
		System.out.println();
		
	}

	LocalDeclaration around(ForeachStatement foreachStatement):
		withincode(public RecoveredElement AssistParser.buildInitialRecoveryState())
			&& get (LocalDeclaration ForeachStatement.elementVariable)
			&& target(foreachStatement) {
	    LocalDeclaration result = proceed(foreachStatement);

	    MessageSend messageSend = new MessageSend();
	    messageSend.receiver = new QualifiedTypeReference(JxtendTypeConstants.COLLECTION_INFERENCE_UTILS,
	    		ASTNodeUtils.encodePosArray(JxtendTypeConstants.COLLECTION_INFERENCE_UTILS.length, 0));
	    messageSend.selector = "elementType".toCharArray();
	    messageSend.arguments = new Expression[] { foreachStatement.collection };

	    result.initialization = messageSend;
	    return result;
	}

//	after(MethodDeclaration md, CompilationUnitDeclaration unit, AssistParser assistParser):
//		execution(public void AssistParser.parseBlockStatements(MethodDeclaration, CompilationUnitDeclaration))
//        	&& args(md, unit)
//        	&& this(assistParser) {
//		md.bodyStart = (md.statements.length > 1) ? md.statements[md.statements.length - 2].sourceEnd + 1: md.bodyStart;
//    	md.bodyEnd = assistParser.assistNode.sourceEnd + 1;
//
//    	assistParser.parse(md, unit);
//	}

	
//	before(SourceElementParser parser, SourceElementNotifier notifier, CompilationUnitDeclaration parsedUnit):
//		withincode(public CompilationUnitDeclaration SourceElementParser.parseCompilationUnit(
//					ICompilationUnit, boolean, IProgressMonitor))
//			&& this(parser)
//			&& call(public void SourceElementNotifier.notifySourceElementRequestor(
//						CompilationUnitDeclaration, int, int, boolean, HashtableOfObjectToInt, Map))
//			&& target(notifier)
//			&& args(parsedUnit, int, int, boolean, HashtableOfObjectToInt, Map) {
//		if (!(parser instanceof IndexingParser)) {
//			return;
//		}
//
//		ReadWriteMonitor monitor = getMonitor(notifier);
//		monitor.exitWrite();
//
//		try {
//			Field field = ReflectUtils.getPublicField(SourceElementParser.class, "lookupEnvironment");
//			LookupEnvironment lookupEnvironment = (LookupEnvironment) ReflectUtils.fieldValue(field, parser);
//	
//			String fileName = new String(parsedUnit.compilationResult.compilationUnit.getFileName());
//			assert fileName.startsWith("/") && fileName.indexOf('/', 1) != -1;
//			
//			WorkingCopyOwner workingCopyOwner = DefaultWorkingCopyOwner.PRIMARY.primaryBufferProvider;
//			
//			String projectName = fileName.substring(1, fileName.indexOf('/', 1));
//			IJavaProject javaProject = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProject(projectName);
//	
//			AnnotationProcessorInvoker.invoke(parser, (JavaProject) javaProject, workingCopyOwner,
//					parsedUnit, lookupEnvironment, true, true);
//		} finally {
//			monitor.enterWrite();
//		}
//	}
//
//	private static ReadWriteMonitor getMonitor(SourceElementNotifier notifier) {
//		ISourceElementRequestor requestor = requestorOfSourceElementNotifier.get(notifier);
//		SourceIndexer indexer = indexerOfSourceIndexerRequestor.get((SourceIndexerRequestor) requestor);
//		SearchDocument document = documentOfAbstractIndexer.get(indexer);
//		Index index = indexOfSearchDocument.get(document);
//		return index.monitor;
//	}

//    ImportBinding around(CompilationUnitScope compilationUnitScope,
//    			char[][] compoundName, boolean isOnDemand, Binding binding, ImportReference reference):
//    		call(public ImportBinding.new(char[][], boolean, Binding, ImportReference))
//    			&& withincode(void CompilationUnitScope.checkAndSetImports()) && this(compilationUnitScope)
//    			&& args(compoundName, isOnDemand, binding, reference) {
////    	if (Thread.currentThread().getName().equals("main")
////    			|| Thread.currentThread().getName().equals("org.eclipse.jdt.internal.ui.text.JavaReconciler")) {
//	    	if (binding == null) {
//	    		binding = compilationUnitScope.findImport(compoundName, reference.isStatic(), isOnDemand);
//	    	}
////    	}
//
//    	return proceed(compilationUnitScope, compoundName, isOnDemand, binding, reference);
//    }

//	before(TypeVariableBinding typeVariableBinding): execution(public TypeBinding TypeVariableBinding.erasure())
//			&& this(typeVariableBinding) {		
//		if ((typeVariableBinding.superclass == null)
//				&& Thread.currentThread().getName().equals("org.eclipse.jdt.internal.ui.text.JavaReconciler")) {
//			LookupEnvironment lookupEnvironment = extractLookupEnvironment(typeVariableBinding);
//			typeVariableBinding.superclass = lookupEnvironment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null);
//		}
//	}
//
//	private static LookupEnvironment extractLookupEnvironment(TypeVariableBinding typeVariableBinding) {
//		try {
//			Field environmentField = TypeVariableBinding.class.getDeclaredField("environment");
//			environmentField.setAccessible(true);
//			return (LookupEnvironment) environmentField.get(typeVariableBinding);
//		} catch (SecurityException e) {
//			throw new JxtendEclipseHookException(e);
//		} catch (NoSuchFieldException e) {
//			throw new JxtendEclipseHookException(e);
//		} catch (IllegalArgumentException e) {
//			throw new JxtendEclipseHookException(e);
//		} catch (IllegalAccessException e) {
//			throw new JxtendEclipseHookException(e);
//		}
//	}
//
//	LocalVariableBinding around(SingleNameReference singleNameReference):
//		execution(public LocalVariableBinding SingleNameReference.localVariableBinding())
//			&& this(singleNameReference) {
//		if (singleNameReference.binding instanceof FieldBinding
//				&& (singleNameReference.bits & ASTNode.RestrictiveFlagMASK) == Binding.LOCAL) {
//			return null;
//		}
//
//		return proceed(singleNameReference);
//	}
//
//	before(TypeBinding argumentType):
//		execution(public int TypeVariableBinding.boundCheck(Substitution, TypeBinding))
//			&& args(Substitution, argumentType) {
//		if ((argumentType instanceof WildcardBinding)
//				&& ((WildcardBinding) argumentType).bound instanceof TypeVariableBinding) {
//			TypeVariableBinding typeVariableBinding = (TypeVariableBinding) ((WildcardBinding) argumentType).bound;
//			if (typeVariableBinding.superclass == null) {
//				LookupEnvironment lookupEnvironment = extractLookupEnvironment(typeVariableBinding);
//				typeVariableBinding.superclass =
//						lookupEnvironment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null);
//			}
//		}
//	}
//
//	TypeBinding around(TypeVariableBinding self): call(public TypeBinding TypeBinding.erasure()) && target(self)
//			&& withincode(public TypeBinding ArrayBinding.erasure()) {
//		TypeBinding result = proceed(self);
//
//		if (result == null) {
//			LookupEnvironment lookupEnvironment = extractLookupEnvironment(self);
//			result = lookupEnvironment.getResolvedType(TypeConstants.JAVA_LANG_OBJECT, null);			
//		}
//
//		return result;
//	}

//	ArrayBinding
//	public TypeBinding erasure() {
//	    TypeBinding erasedType = this.leafComponentType.erasure();
	
	
//    List<ImportDeclaration> around(org.eclipse.jdt.core.dom.CompilationUnit root):
//    	call(List (org.eclipse.jdt.core.dom.CompilationUnit).imports()) && target(root) {
////    		&& withincode(private IRegion ImportRewriteAnalyzer.evaluateReplaceRange(
////    				org.eclipse.jdt.core.dom.CompilationUnit)) {
//    	List result = proceed(root);
//    	return result;
//	}
//
//    IRegion around(): execution(private IRegion ImportRewriteAnalyzer.evaluateReplaceRange(
//    		org.eclipse.jdt.core.dom.CompilationUnit)) {
//    	System.out.println("@@@ YOYO");
//    	return proceed();
//    }
//
//    List around(): execution(public List (org.eclipse.jdt.core.dom.CompilationUnit).imports()) {
//    	JoinPoint joinPoint = thisJoinPoint;
//    	List result = proceed();
//    	return result;
//	}

    
    
//    public TypeBinding CompletionOnQualifiedNameReference.superResolveType(BlockScope scope) {
//		return super.resolveType(scope);
//    }
//
//    TypeBinding around(CompletionOnQualifiedNameReference completionOnQualifiedNameReference, BlockScope scope):
//    	execution(TypeBinding CompletionOnQualifiedNameReference.resolveType(BlockScope))
//    		&& this(completionOnQualifiedNameReference) && args(scope) {
//
////		char[][] newTokens = new char[originalTokens.length + 1][];
////		System.arraycopy(originalTokens, 0, newTokens, 0, originalTokens.length);
////		newTokens[newTokens.length - 1] = completionOnQualifiedNameReference.completionIdentifier;
//
//    	if (AnnotationProcessorInvoker.isProcessingInCurrentThread()) {
//    		char[][] originalTokens = completionOnQualifiedNameReference.tokens;
//    		char[][] newTokens = new char[originalTokens.length + 1][];
//    		System.arraycopy(originalTokens, 0, newTokens, 0, originalTokens.length);
//    		newTokens[newTokens.length - 1] = completionOnQualifiedNameReference.completionIdentifier;
//
//    		try {
//    			completionOnQualifiedNameReference.tokens = newTokens;
//
//    			Method superResolveTypeMethod;
//    			try {
//    				completionOnQualifiedNameReference.bits |= Binding.FIELD;
//
//    				superResolveTypeMethod = completionOnQualifiedNameReference.getClass().getDeclaredMethod(
//    						"superResolveType", BlockScope.class);
//    				return (TypeBinding) superResolveTypeMethod.invoke(completionOnQualifiedNameReference, scope);
//    			} catch (SecurityException e) {
//    				throw new JxtendReflectionException(e.getMessage(), e);
//    			} catch (NoSuchMethodException e) {
//    				throw new JxtendReflectionException(e.getMessage(), e);
//    			} catch (IllegalArgumentException e) {
//    				throw new JxtendReflectionException(e.getMessage(), e);
//    			} catch (IllegalAccessException e) {
//    				throw new JxtendReflectionException(e.getMessage(), e);
//    			} catch (InvocationTargetException e) {
//    				throw new JxtendReflectionException(e.getMessage(), e);
//    			}
//
////    			return proceed(completionOnQualifiedNameReference, scope);
//    		} finally {
//    			completionOnQualifiedNameReference.tokens = originalTokens;
//    		}
////    		char[][] newTokens = new char[tokens.length + 1][];
////    		System.arraycopy(tokens, 0, newTokens, 0, tokens.length);
////    		newTokens[newTokens.length - 1] = completionOnQualifiedNameReference.completionIdentifier;
////
////    		completionOnQualifiedNameReference.tokens = newTokens;
////    	} else {
////    		if (tokens[tokens.length - 1] == completionOnQualifiedNameReference.completionIdentifier) {
////    			char[][] newTokens = new char[tokens.length - 1][];
////    			System.arraycopy(tokens, 0, newTokens, 0, tokens.length - 1);
////
////    			completionOnQualifiedNameReference.tokens = newTokens;
////    		}
//
//		} else {
//			return proceed(completionOnQualifiedNameReference, scope);
//		}
//
////		return proceed(completionOnQualifiedNameReference, scope);
//
//	}
}
